/*
 * vmdev.c
 */

#include <stdio.h>
#include "vmbdoor.h"

/*
 *	Get device information
 *	returns 1 if device is present
 */

int	GetDeviceSub(
	int dev,							/* device number 0 - 51(?)			*/
	char *buf)							/* 40 bytes device info area		*/
{
	uint32 info;
	uint32 command = VMCMD_GET_DEVICE;
	int pos = 0;
	
	while (pos < 40) {
		info = dev;
		info <<= 16;
		info |= pos;					/* H.W. = dev #, L.W. = offset		*/
		
		if (VMBackDoor(NULL, &info, &command, NULL) != 1) {
			return 0;
		}
		
		*((uint32 *)(buf + pos)) = info;
		pos += 4;
	}
	
	return 1;
}

/*
 * Connect/Disconnect device
 * returns 1 on successful operation
 */
 
int SetDeviceSub(
	int dev,							/* device number					*/
	int mode)							/* 0:disconnect ~0:connect			*/
{
	char buf[40];
	uint32 device;
	uint32 command;
	
	/* it seems that setting device state without first getting information	*/
	/* may fail in certain cases, e.g. right after VMware startup.			*/

	if (GetDeviceSub(dev, buf) == 0) {
		return 0;
	}

	device = dev;						/* first, disconnect anyway			*/
	command = VMCMD_SET_DEVICE;
	
	if (VMBackDoor(NULL, &device, &command, NULL) != 1) {
		return 0;
	}
	
	if (mode) {							/* connect operation ?				*/
		device = dev | 0x80000000;;
		command = VMCMD_SET_DEVICE;

		if (VMBackDoor(NULL, &device, &command, NULL) != 1) {
			return 0;
		}
	}
	return 1;
}

/*
 *	print device list
 */
void DeviceList()
{
	int idx = 0;
	char buf[40];

	for (idx = 0; idx <= 10; idx++) {
		if (GetDeviceSub(idx, buf) != 1 || buf[0] == 0) {
			break;
		}

		printf("  %c%d\t%s\n", buf[36] ? '+' : ' ', idx, buf);
	}
}

/*
 * print usage help
 */
void Usage()
{
	printf(
		"Usage: vmdev [<action>][<target>]\n"
		"\n"
		"<action>\n"
		"  +\t(Re)connect target device(s). This is the default.\n"
		"  -\tDisconnect target device(s).\n"
		"\n"
		"<target>\n"
		"  0..9\tTarget device number. \n"
		"  *\tRepresents all devices. This is the default.\n"
		"\n"
		"When both <action> and <target> options are omitted, \n"
		"current device state list is shown.\n"
		"Connected devices are marked with \"+\" in the list.\n"
	);
}

/*
 * main
 */
int main (int argc, char **argv)
{
	int connect = 1;
	
	/* are we running in VMware ? */
	if (VMGetVersion() == 0) {
		printf("Not running inside VMware.\n");
		return 0;
	}

	/* any command line options ? */
	if (argc > 1) {
		argv++;
		
		if (**argv == '+') {
			(*argv)++;
		}
		else if (**argv == '-') {
			(*argv)++;
			connect = 0;
		}

		if (**argv == '*' || **argv == '\0') {
			int i;
			
			for (i = 0; i < 10; i++) {
				SetDeviceSub(i, connect);
			}
		}
		else if (**argv >= '0' && **argv <= '9') {
			SetDeviceSub(**argv - '0', connect);
		}
		else {
			Usage();
			return 0;
		}
	}

	DeviceList();
	
	return 1;
}
/* End of File */
